﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
using Cofe.IO.Interfaces;
using Cofe.Core;
using System.Text.RegularExpressions;
using Cofe.Core.Actions;
using Cofe.Core.Utils;

namespace Cofe.IO.Compress
{
#if NETFX_CORE
    public class SystemIOCompressionWrapper : ICompressorWrapper
    {
        #region Constructor

        #endregion

        #region Methods

        public Task<bool> TestAsync(StreamContainer container)
        {
            return Task.FromResult<bool>(true);
        }

        public bool Exists(StreamContainer container, string pathOrMask, bool isFolder)
        {
            pathOrMask = PathFE.ConvertSlash(pathOrMask, '/');

            if (pathOrMask.Contains("*"))
            {
                string listPattern = String.Format(RegexPatterns.CompressionListPattern, Regex.Escape(pathOrMask.Replace("*", "")));
                return List(container, listPattern, true).Any();
            }

            lock (_lockObject)
            {
                using (ZipArchive archive = new ZipArchive(container.Stream, ZipArchiveMode.Read))
                {
                    return archive.GetEntry(pathOrMask) != null;
                }
            }
        }

        /// <summary>
        /// List entries that match the filterStr, the filterStr must be in format of RegexPatterns.SevenZipListPattern.
        /// </summary>
        /// <param name="filterStr"></param>
        /// <returns></returns>
        public IEnumerable<List<PropertyPair>> List(StreamContainer container, string pattern, bool listSubdir = false)
        {
            if (pattern.IndexOf("?<name>") == -1)
                throw new ArgumentException("");
            if (pattern.IndexOf("?<trail>") == -1)
                throw new ArgumentException("");
            if (pattern.IndexOf("?<parent>") == -1)
                throw new ArgumentException("");

            List<string> returnedPathList = new List<string>();
            if (listSubdir)
                throw new NotImplementedException();

            lock (_lockObject)
            {
                using (ZipArchive archive = new ZipArchive(container.Stream, ZipArchiveMode.Read))
                {
                    List<string> directoryPath = new List<string>();

                    Func<string, bool> isMatch = (path) =>
                        {
                            Match match = new Regex(pattern).Match(path);
                            if (match.Success)
                            {
                                string parent = match.Groups["parent"].Value;
                                string name = match.Groups["name"].Value;
                                string trail = match.Groups["trail"].Value;

                                if (String.IsNullOrEmpty(trail))
                                    return true;
                            }
                            return false;
                        };


                    foreach (ZipArchiveEntry entry in archive.Entries)
                    {
                        string fullName = PathFE.ConvertSlash(entry.FullName, '\\');

                        string path = PathFE.GetDirectoryName(fullName);
                        if (!(directoryPath.Contains(path, StringComparer.CurrentCultureIgnoreCase)))
                            if (isMatch(path))
                            {
                                yield return new List<PropertyPair>()
                                    {
                                        PropertyPair.FromValue(CofeProperties.DefaultInterfaceType, typeof(IDirectoryInfo)),
                                        PropertyPair.FromValue(FileSystemInfoProperties.RelativePath, path),
                                        PropertyPair.FromValue(CofeProperties.Name, PathFE.GetFileName(path))
                                    };
                            }
                    }

                    foreach (ZipArchiveEntry entry in archive.Entries)
                    {
                        string fullName = PathFE.ConvertSlash(entry.FullName, '\\');

                        if (isMatch(fullName))
                        {
                            yield return new List<PropertyPair>()
                                    {
                                        PropertyPair.FromValue(CofeProperties.DefaultInterfaceType, typeof(IFileInfo)),
                                        PropertyPair.FromValue(FileSystemInfoProperties.RelativePath, fullName),
                                        PropertyPair.FromValue(CofeProperties.Name, entry.Name)
                                    };
                        }

                    }
                }
            }
        }

        public bool ExtractOne(StreamContainer container, string fileName, string password, System.IO.Stream outputStream)
        {
            fileName = PathFE.ConvertSlash(fileName, '/');

            lock (_lockObject)
            {
                using (ZipArchive archive = new ZipArchive(container.Stream, ZipArchiveMode.Read))
                {
                    var entry = archive.GetEntry(fileName);
                    using (var inputStream = entry.Open())
                        StreamUtils.CopyStream(inputStream, outputStream, false, false, true);
                }
            }

            return true;
        }

        public bool CompressMultiple(StreamContainer container, Dictionary<string, System.IO.Stream> streamDic,
            IProgress<ProgressEventArgs> progress = null)
        {
            lock (_lockObject)
            {

                try
                {
                    using (ZipArchive archive = new ZipArchive(container.Stream, ZipArchiveMode.Update))
                    {
                        foreach (var file in streamDic)
                        {
                            string fileName = PathFE.ConvertSlash(file.Key, '/');

                            var entry = archive.GetEntry(fileName);
                            if (entry == null)
                                entry = archive.CreateEntry(fileName);

                            using (var inputStream = entry.Open())
                                StreamUtils.CopyStream(file.Value, inputStream, false, false, false);
                        }
                    }

                    return true;

                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);                    
                    return false;
                }

            }
        }

        public bool Delete(StreamContainer container, string delPathOrMask)
        {
            delPathOrMask = PathFE.ConvertSlash(delPathOrMask, '/');

            lock (_lockObject)
            {
                try
                {
                    using (ZipArchive archive = new ZipArchive(container.Stream, ZipArchiveMode.Update))
                    {
                        var entries = archive.Entries.ToArray();
                        foreach (var entry in entries)
                            if (PathFE.MatchFileMask(entry.FullName, delPathOrMask))
                                entry.Delete();
                    }

                    return true;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);                    
                    return false;                    
                }
            }
        }

        public bool IsArchive(string fileName)
        {
            string type = PathFE.GetExtension(fileName).ToLower().TrimStart('.');
            switch (type)
            {
                case "zip":
                    return true;
            }
            return false;
        }

        public byte[] GetEmptyArchiveBytes(string type)
        {
            switch (type)
            {
                case "zip": return CompressionUtils.EmptyZip;
            }
            return null;
        }


        #endregion

        #region Data
        object _lockObject = new object();

        #endregion

        #region Public Properties

        #endregion

    }
#endif
}
